map = null
layers = {}
config =
  center:
    lon: 11.57631
    lat: 48.13373
    ml:
      lon: 11.57631 + 0.01 + 0.04 /2
      lat: 48.13373 + 0.003 + 0.027 / 2
  provider: 'google'
  lon: 11.57631
  lat: 48.13373
  zoom: 13
  rect:
    lon: 11.57631 + 0.01
    lat: 48.13373 + 0.003
  w: 0.04
  h: 0.027
  size: 20
  max_size: 28
  frame_size: 1
  max_frame_size: 10
  renderers: ["SVG", "Canvas", "VML"]

generate_rects = (lon, lat, size, dw, dh) ->
  result = []
  for i in [0...size]
    for j in [0...size]
      result.push({ 
        type: 'Polygon'
        coordinates: [generate_rect(lon + i * dw, lat + j * dh, dw, dh)] 
      })
  result

generate_rect = (lon, lat, w, h) ->
  [[lon, lat], [lon + w, lat], [lon + w, lat + h], [lon, lat + h]]
  # [lon, lat, lon + w, lat + h]

generate_feature = (lon, lat, frame_size, rect_size, dw, dh) ->
  result = []
  for k in [0...frame_size]
    do (lon = lon + k * config.w / 5.7 , lat = lat + k * config.h / 5.7) ->
      result.push({
        type: 'Feature'
        properties: 
          line: 0.5
          opacity: 0
        geometry:
          type: 'GeometryCollection'
          geometries: [
            type: 'Polygon'
            coordinates: [generate_rect(lon, lat, dw * rect_size, dh * rect_size)]
          ]
      })
      for i in [0...rect_size]
        for j in [0...rect_size]
          result.push({ 
            type: 'Feature'
            properties:
              line: 0
              opacity: Math.random() / 3
              color: 'red'
            geometry:
              type: 'GeometryCollection'
              geometries: [
                type: 'Polygon'
                coordinates: [generate_rect(lon + i * dw, lat + j * dh, dw, dh)]
              ]
          })
  console.log result.length
  result

getFeature = () ->
  result =
    type: "FeatureCollection", 
    features: generate_feature(
      config.rect.lon, 
      config.rect.lat, 
      config.frame_size, 
      config.size, 
      config.w / config.size, 
      config.h / config.size
    )
 
init = () ->
  map = new OpenLayers.Map 'map',
    displayPosition: normProjection()
    eventListeners:
      'zoomend': mapEvent
      'moveend': mapEvent
  map.addControl new OpenLayers.Control.ScaleLine()

mapEvent = (e) ->
  # to ignore weird map.getCenter return value during map.addLayer
  if layers.base 
    switch e.type
      when "zoomend"
        config.zoom = map.getZoom()
      when 'moveend'
        center = projectedPoint2 map.getCenter()
        config.lon = center.lon
        config.lat = center.lat
    # console.log e.type, config
  
normProjection = () ->
  new OpenLayers.Projection 'EPSG:4326'

currProjection = () ->
  map.getProjectionObject()

projectedPoint = (lon, lat) ->
  point = new OpenLayers.LonLat(lon, lat)
  [norm, curr] = [normProjection(), currProjection()]
  point.transform(norm, curr) unless norm.projCode == curr.projCode
  point

projectedPoint2 = (point) ->
  [norm, curr] = [normProjection(), currProjection()]
  point.transform(curr, norm) unless norm.projCode == curr.projCode
  point

geoJsonFormat = () ->
  new OpenLayers.Format.GeoJSON
    externalProjection: normProjection()
    internalProjection: currProjection()

@addFeature = () ->
  style = new OpenLayers.Style({
    strokeColor: '#333'
    strokeOpacity: '${line}'
    strokeWidth: '${line}'
    fillOpacity: '${opacity}',
    fillColor:'${color}'
  })
  styleMap = new OpenLayers.StyleMap(style)

  features = getFeature()
  unless layers.vector
    layer = new OpenLayers.Layer.Vector("ML", { renderers: config.renderers, styleMap: styleMap })
    layers.vector = layer
    map.addLayer layer
    layer.addFeatures geoJsonFormat().read(features)

@removeFeature = () -> 
  if layers.vector
    # layers.vector.removeAllFeatures()
    map.removeLayer layers.vector
    layers.vector.destroy()
    layers.vector = null 

@centerMap = (options = {}) ->
  if options.ml
    map.setCenter(projectedPoint(config.center.ml.lon, config.center.ml.lat), config.zoom)
  else
    map.setCenter(projectedPoint(config.center.lon, config.center.lat), config.zoom)

@setProvider = (provider) ->
  if layer = providerToLayer(provider)
    layers.base && map.removeLayer layers.base, false
    layers.base = null
    map.addLayer layer
    layers.base = layer
    centerMap()
    map.resetLayersZIndex()
  redrawDf map
  redraw()

providerToLayer = (provider) ->
  name = "Base Layer"
  { osm: () ->
      new OpenLayers.Layer.OSM([
        "http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
        "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
        "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"])
    wms: () ->
      new OpenLayers.Layer.WMS(name, 
        "http://vmap0.tiles.osgeo.org/wms/vmap0",
        { layers: 'basic'} )
    google: () ->
      new OpenLayers.Layer.Google(name,
        { type: google.maps.MapTypeId.ROADMAP, numZoomLevels: 20 })
    bing: () ->
      new OpenLayers.Layer.Bing(
        name: name,
        type: 'Road' #AerialWithLabels'
        key: 'AsFA8iwZIHFOYEd_o7GkRlY9Y5sNzynSUt_0_-Cio1R0ndXeEU1sgiAb6kyvuRY2')
    tms: () ->
      new OpenLayers.Layer.TMS(name,
        "http://tilecache.osgeo.org/wms-c/Basic.py/", 
        { layername: "basic", type: "png" })
  }[provider]()

setRenderer = (renderer) ->
  config.renderers = switch (renderer) 
                     when 'canvas' then ["Canvas", "SVG", "VML"]
                     else ["SVG", "Canvas", "VML"]
  redraw()

setRectSize = (size) ->
  config.size = ~~size
  redraw()

setFrameSize = (size) ->
  config.frame_size = ~~size
  redraw()

redraw = () ->
  removeFeature()
  addFeature()

$ () -> 
  init()
  for e in ['osm', 'google', 'bing', 'wms', 'tms']
    $('#provider').append("<option value='#{e}'>#{e}</option>")
  for e in ['svg', 'canvas']
    $('#renderer').append("<option value='#{e}'>#{e}</option>")
  for e in [1..config.max_size]
    $('#rectsize').append("<option value='#{e}'>#{e}</option>")
  for e in [1..config.max_frame_size]
    $('#framesize').append("<option value='#{e}'>#{e}</option>")
  $(document).on 'change', '#provider', (e) -> setProvider $(e.target).val()
  $(document).on 'change', '#renderer', (e) -> setRenderer $(e.target).val()
  $(document).on 'change', '#rectsize', (e) -> setRectSize $(e.target).val()
  $(document).on 'change', '#framesize', (e) -> setFrameSize $(e.target).val()
  $(document).on 'click', '#add-data', (e) -> addFeature()
  $(document).on 'click', '#rm-data', (e) -> removeFeature()
  $('#provider').val(config.provider).trigger('change')
  $('#renderer').val(config.renderers[0].toLowerCase())
  $('#rectsize').val(config.size)
  $('#framesize').val(config.frame_size)
  $('input[min]').each () ->
    @onchange = (e) -> 
      value = e.target.value
      $('.largemap').css('-webkit-transform': "rotate(#{value}deg)")
  
@bench = (n = 10, timeout = 0, r = 'svg') ->
  config.size = config.max_size
  config.frame_size = config.max_frame_size
  $('#rectsize').val(config.size)
  $('#framesize').val(config.frame_size)
  $('#renderer').val(r).trigger('change')

  now = () -> new Date()
  test = (i, next) -> 
    ts_i = now()
    redraw()
    i = i + 1
    console.log r, i, '>>>', (now() - ts_i) / 1000, 's'
    setTimeout((() -> next(i)), timeout * 1000)
  next = (i) ->
    if i == n
      console.log r, 'end >>>', (now() - ts_start) / 1000, 'seconds'
      removeFeature()
    else
      test i, next
  ts_start = now()
  console.log r, 'start >>>', ts_start
  test 0, next


redrawDf = (map) ->
  if layers.device
    map.removeLayer layers.device
    layers.device.destroy()
    layers.device = null 

  lon = config.lon
  lat = config.lat
  ptA = new OpenLayers.LonLat(lon, lat)
  ptB = new OpenLayers.LonLat(lon + 10, lat + 6)
  [norm, curr] = [normProjection(), currProjection()]
  ptA = ptA.transform norm, curr
  ptB = ptB.transform norm, curr

  defaultStyles =
    df:
      graphicZIndex: 10,
      strokeColor: 'steelblue',
      fillColor: 'steelblue',
      fillOpacity: 1,
      label: name,
      labelAlign: 'cb',
      labelYOffset: -20,
      fontSize: '11px',
      fontWeight: 'bold',
      fontColor: '#555'
    line: 
      graphicZIndex: 5,
      strokeColor: '#555',
      strokeOpacity: 1,
      strokeWidth: 1
  styles = 
    df: 
      graphicWidth: 20,
      graphicHeight: 20,
      externalGraphic: 'app/images/arrow_blue.png',
      fillOpacity: 1
    line: 
      strokeColor: '#555',
      strokeOpacity: 1,
      strokeWidth: 2

  layer = new OpenLayers.Layer.Vector('device', { rendererOptions: { yOrdering: true }})
  layers.device = layer
  map.addLayer layer

  dfFeature = new OpenLayers.Feature.Vector(
    new OpenLayers.Geometry.Point(ptA.lon, ptA.lat),
    null,
    _.extend({}, OpenLayers.Feature.Vector.style['default'], defaultStyles.df, styles.df, { rotation: 80 })
  );

  lineFeature = new OpenLayers.Feature.Vector(
    new OpenLayers.Geometry.LineString([
      new OpenLayers.Geometry.Point(ptA.lon, ptA.lat),
      new OpenLayers.Geometry.Point(ptB.lon, ptB.lat)
    ]),
    null,
    _.extend({}, defaultStyles.line, styles.line)
  )
  layer.addFeatures [lineFeature, dfFeature]

